#import "../tpl.typ": conf
#show: doc => conf(
  ["可撤销的 Proxy"],
  doc
)

= 可撤销的 Proxy

`Proxy` 用于创建代理对象，而 `Proxy.revocable()` @proxy-revocable 可以创建*可撤销*的代理对象。

它的语法如下：

```js
const {
  proxy,
  revoke,
} = Proxy.revocable(target, handler)
```

其中的 `proxy`, `target` 和 `handler` 和普通代理对象一模一样，只是多了一个 `revoke` 撤销函数。

`revoke` 函数无需入参，一旦调用，便会切断 `proxy` 和 `target` 的代理关系。

#figure(
  image("proxy-revocable.png", width: 80%),
  caption: [`revoke()` 切断 `target` 和 `proxy` 的关系]
) <revoke>

使用撤销函数可以方便的控制代理对象的生命周期。举个例子：

```js
const {
  proxy,
  invoke,
} = Proxy.revocable(
  {},
  {
    get(target, name) {
      return `[[${name}$]]`;
    }
  }
)

console.log(proxy.foo); // "[[foo]]"

revoke(); // 撤销 Proxy 的代理资质

console.log(proxy.foo);
// Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked

proxy.bar = 1;
// Uncaught TypeError: Cannot perform 'set' on a proxy that has been revoked

delete proxy.foo
// Uncaught TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked

typeof proxy
// "object"，因为 typeof 不会触发任何 trap 方法
```

可以看到，调用撤销函数后，代理对象将不再可用，对它的大部分操作都会抛出错误。

在实际代码中，`immer` 库的代码 @immer-proxy 使用了可撤销的代理。

#figure(
  image("immer-proxy.png"),
  caption: [`immer` 中的可撤销代理]
)

#bibliography("refs.yml", title: "参考资料")